﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace Dotnet.Utils.Utility.CollectionUtil;

public class BaseExpressionVisitor : ExpressionVisitor
{
    private readonly Dictionary<ParameterExpression, ParameterExpression> map;

    /// <summary>
    ///
    /// </summary>
    /// <param name="map"></param>
    public BaseExpressionVisitor(Dictionary<ParameterExpression, ParameterExpression> map)
    {
        this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="map"></param>
    /// <param name="exp"></param>
    /// <returns></returns>
    public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
    {
        return new BaseExpressionVisitor(map).Visit(exp);
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (map.TryGetValue(node, out ParameterExpression replacement))
        {
            node = replacement;
        }
        return base.VisitParameter(node);
    }
}

public static class ExpressionHelper
{
    public static Expression<Func<T, bool>> True<T>()
    { return param => true; }

    public static Expression<Func<T, bool>> False<T>()
    { return param => false; }

    public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate)
    { return predicate; }

    /// <summary>
    /// and扩展
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="first"></param>
    /// <param name="second"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        //return first.Compose(second, Expression.Add);
        return first.Compose(second, Expression.AndAlso);
    }

    /// <summary>
    /// addelse扩展
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="first"></param>
    /// <param name="condition"></param>
    /// <param name="second"></param>
    /// <param name="third"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> AndElse<T>(this Expression<Func<T, bool>> first,
        bool condition,
        Expression<Func<T, bool>> second,
        Expression<Func<T, bool>> third)
    {
        return first.Compose(condition ? second : third, Expression.AndAlso);
    }

    /// <summary>
    /// or扩展
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="first"></param>
    /// <param name="second"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        //return first.Compose(second, Expression.Or);
        return first.Compose(second, Expression.OrElse);
    }

    /// <summary>
    /// OrElse扩展
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="first"></param>
    /// <param name="second"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> OrElse<T>(this Expression<Func<T, bool>> first,
        bool condition,
        Expression<Func<T, bool>> second,
        Expression<Func<T, bool>> third)
    {
        return first.Compose(condition ? second : third, Expression.OrElse);
    }

    /// <summary>
    /// Not
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="expression"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
    {
        return Expression.Lambda<Func<T, bool>>(Expression.Not(expression.Body), expression.Parameters);
    }

    /// <summary>
    /// Lambda表达式拼接
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="first"></param>
    /// <param name="second"></param>
    /// <param name="merge"></param>
    /// <returns></returns>
    public static Expression<T> Compose<T>(this Expression<T> first,
        Expression<T> second,
        Func<Expression, Expression, Expression> merge)
    {
        var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
        var secondBody = BaseExpressionVisitor.ReplaceParameters(map, second.Body);
        return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
    }

    public static IQueryable<T> FilterFields<T>(this IQueryable<T> query, List<string> fields, string paramName = "m")
    {
        //定义表达式树中的变量(linq表达式参数的名称，如m=>m.Name中的m)
        var param = Expression.Parameter(typeof(T), paramName);
        /* typeof(T).GetProperty(m):获取成员属性信息
         * Expression.Property(param, m):对象T中的成员属性
         * Expression.Bind:给字段或属性初始化并赋值
         */
        var bindings = fields.Select(m => Expression.Bind(typeof(T).GetProperty(m), Expression.Property(param, m)));
        /* Expression.New(typeof(T)):调用无参构造函数
         * Expression.MemberInit:实例化对象并为属性设置值
         */
        var newEnt = Expression.MemberInit(Expression.New(typeof(T)), bindings);
        //创建lambda表达式(Func<T,T>:参数类型为T,返回类型为T)
        var lambda = Expression.Lambda<Func<T, T>>(newEnt, param);
        return query.Select(lambda);
    }

    public static IQueryable<T> NoDelete<T>(this IQueryable<T> query, string fieldName = "IsDelete", string paramName = "m")
    {
        //创建表达式中的参数或变量(如m=>m.IsDelete==false中的m)
        var param = Expression.Parameter(typeof(T), paramName);
        //创建表达式中左边的属性(如m=>m.IsDelete==false中的m.IsDelete)
        var left = Expression.Property(param, fieldName);
        //创建表达式中右边的常量表达式(如m=>m.IsDelete==false中的false)
        var right = Expression.Constant(false);
        //创建成二元运算符表达式(如m=>m.IsDelete==false中的==)
        var body = Expression.Equal(left, right);
        //拼接成筛选条件
        return query.Where(Expression.Lambda<Func<T, bool>>(body, param));
    }

    public static Expression<Func<T, bool>> GetConditionExpression<T>(string[] options, string fieldName)
    {
        ParameterExpression left = Expression.Parameter(typeof(T), "c");//c=>
        Expression expression = Expression.Constant(false);
        foreach (var optionName in options)
        {
            Expression right = Expression.Call
                   (
                      Expression.Property(left, typeof(T).GetProperty(fieldName)),  //c.DataSourceName
                      typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),// 反射使用.Contains()方法
                     Expression.Constant(optionName)           // .Contains(optionName)
                   );
            expression = Expression.Or(right, expression);//c.DataSourceName.contain("") || c.DataSourceName.contain("")
        }
        Expression<Func<T, bool>> finalExpression
            = Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left });
        return finalExpression;
    }
}

public static class DynamicLinqExpressions
{
    public static Expression<Func<T, bool>> True<T>()
    { return f => true; }

    public static Expression<Func<T, bool>> False<T>()
    { return f => false; }

    public static Expression<Func<T, bool>> Or<T>(Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>
              (Expression.Or(expr1.Body, invokedExpr), expr1.Parameters);
    }

    public static Expression<Func<T, bool>> And<T>(Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>
              (Expression.And(expr1.Body, invokedExpr), expr1.Parameters);
    }
}